import config from '@/config';
import $store from '@/store';
import { $XTrack } from '@/report';

/**
 * options:{
 *   report: true     // 是否上报埋点数据  接口开始请求及请求结束的数据 默认false不上传
 *   alarm: true     // 是否报警 接口异常时报警 默认true 报警
 * }
 * @param options
 * @returns {Promise<unknown>}
 */
export default (options = {}) => {
  options = Object.assign({
    method: 'GET',
    data: {},
    error: true,
    report: true,
    alarm: false,
    loading: false,
    timeout: 10 * 1000, // 设置10秒的超时
    file: false,
    fileName: 'image'
  }, options);

  // console.log('store', store.state)
  return new Promise(async(resolve, reject) => {
    let token = null;
    try {
      if (!['/account/user/iu/oauth/login',
        '/account/user/iu/token',
        '/product/page/viewAttr',
        '/v2/track/time',
        '/v2/track/report'
      ].some((s) => options.url.includes(s))) {
        const { accessToken } = await $store.dispatch('user/login', { api: options.url });
        token = accessToken;
      }
    } catch (e) {
      console.warn('user/login-> ', e);
    }
    const userInfo = uni.getStorageSync(config.userInfoName) || {};

    const { openId, unionId } = userInfo;
    if (openId) {
      options.data = {
        ...(options.data || {}),
        openId,
        unionId
      };
    }

    // 请求开始加载时间
    const startTime = +new Date();
    const requestData = Object.assign({
      requestStartTime: startTime,
      appVersion: config.appVersion,
      appId: config.appId
    }, options.data);

    // 全局配置文件中如果存在storeId，那么优先使用此storeId
    if (config.storeId) {
      requestData.storeId = config.storeId;
    }

    // 删除 null 与 undefined
    for (const key in requestData) {
      if ([null, undefined, 'null', 'undefined'].includes(requestData[key])) {
        delete requestData[key];
      }
    }
    const object = {
      url: /^http/gi.test(options.url) ? options.url : config.serviceApi + options.url,
      timeout: options.timeout,
      enableHttp2: true, // 开启 http2
      header: {
        'accessToken': token,
        'platform': process.env.VUE_APP_PLATFORM // 渠道
      }
    };
    if (options.header) {
      Object.assign(object.header, options.header);
    }

    if (options.file) {
      // 文件上传
      Object.assign(object, {
        filePath: options.file,
        name: options.fileName || 'file',
        formData: requestData
      });
    } else {
      // 普通api
      Object.assign(object, {
        data: requestData,
        responseType: options.responseType || 'text',
        method: options.method.toUpperCase()
      });
    }

    // 是否打印日志
    if (config.LOG) {
      console.info(`${new Date().toLocaleString()}【接口请求：API=${object.url}】body =`, requestData, 'header =', object.header, 'method =', object.method);
    }

    // 接口开始请求埋点记录
    if (options.report) {
      $XTrack('apiRequestStart', {
        url: options.url,
        data: requestData
      });
    }
    if (options.loading) {
      uni.showLoading();
    }
    // 文件上传与普通api
    const request = options.file ? uni.uploadFile : uni.request;
    const requestTask = request({
      ...object,
      success: async res => {
        if (Object.prototype.toString.call(res.data) === '[object ArrayBuffer]') {
          return resolve(res.data);
        }
        const endTime = +new Date();
        if (config.LOG) {
          console.info(`${new Date().toLocaleString()}【接口响应：API=${object.url} 耗时：${endTime - startTime}ms】`, res);
        }
        const { statusCode } = res;
        const data = options.file ? JSON.parse(res.data) : res.data;
        const { code, msg } = data;
        if (statusCode >= 200 && statusCode < 300) {
          // 接口请求结束埋点上报
          if (options.report && !/^(000000|200)$/.test(code)) {
            $XTrack('apiRequestEnd', {
              url: options.url,
              data: requestData,
              statusCode,
              response: {
                code,
                msg
              }
            });
          }
          if (code && !/^(000000|200)$/.test(code)) {
            const errInfo = { code, msg };
            // 是否统一弹出错误提示信息
            if (!/8001|8002/gi.test(code)) {
              if (options.error) {
                uni.showToast({
                  title: errInfo.msg,
                  icon: 'none',
                  duration: 2000
                });
              }
            }
            // 登录状态失效统一处理
            if (/^(8001|8002)/gi.test(code)) {
              console.warn('接口返回8001或8002状态码，确认为登录状态失效，全局清除缓存数据~');
              // 本地有token，但失效了
              if (token) {
                await $store.dispatch('user/login', {
                  forcibly: true
                });
              }
            }
            if (options.alarm && !config.isDev) {
              $store.dispatch('app/setAlarm', {
                code,
                message: errInfo.msg,
                time: [startTime, endTime],
                url: options.url,
                requestData,
                header: object.header
              });
            }
            return reject(errInfo);
          }
          return resolve(data);
        } else {
          const errMsg = '服务器竟然在开小差';
          let errCode = statusCode || 1000;
          if (/timeout/gi.test(res.errMsg)) {
            errCode = 1001;
          }
          res.data = {
            code: errCode,
            message: [errMsg, ' #', errCode].join('')
          };

          if (options.alarm && !config.isDev) {
            $store.dispatch('app/setAlarm', {
              code: statusCode,
              message: res.errMsg || errMsg,
              time: [startTime, endTime],
              url: options.url,
              requestData,
              header: object.header
            });
          }
          reject(res.data);
        }
      },
      fail(err) {
        const { statusCode, errMsg } = err;
        if (/abort/gi.test(errMsg)) {
          return false;
        }
        const endTime = +new Date();
        if (config.LOG) {
          console.warn(`【响应失败：】${new Date().toLocaleString()}【 API=${object.url} 】`, err);
        }

        // 超时相关不进行提示
        if (options.error && !/time/gi.test(errMsg)) {
          uni.showToast({
            title: errMsg || '网络繁忙，请稍后再试',
            icon: 'none',
            duration: 2000
          });
        }

        if (options.alarm && !config.isDev) {
          $store.dispatch('app/setAlarm', {
            code: 'fail',
            message: errMsg || '',
            time: [startTime, endTime],
            url: options.url,
            requestData,
            header: object.header
          });
        }

        // 接口报错埋点记录
        if (options.report) {
          $XTrack('apiRequestEnd', {
            url: options.url,
            data: requestData,
            statusCode,
            errMsg,
            header: object.header
          });
        }
        reject(err);
      },
      complete() {
        if (options.loading) {
          uni.hideLoading();
        }
        if (options.isCancelToken) {
          // 请求取消机制
          $store.dispatch('app/removePending', {
            url: [object.url, JSON.stringify(options.data || {})].join('')
          });
        }
      }
    });
    // 控制消除重复请求
    if (options.isCancelToken) {
      // 请求取消机制
      // console.log('isCancelToken', object.url);
      $store.dispatch('app/removePending', {
        url: [object.url, JSON.stringify(options.data || {})].join(''),
        cancel: true
      });
      $store.dispatch('app/pushPending', {
        url: [object.url, JSON.stringify(options.data || {})].join(''),
        cancel: requestTask
      });
    }
  });
};
